package org.platform.lwc.redis.aop;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.platform.lwc.redis.annotation.Lock;
import org.platform.lwc.redis.utils.SpelUtil;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
/**
 * desc: 解析 自定义 redisson 分布式锁注解
 *
 * @author: 邢阳
 * @mail: xydeveloper@126.com
 * @create 2021-05-28 16:50
 */
@Aspect
@Component
public class LockAspect {
    @Autowired
    private RedissonClient redissonClient;
    /**
     * 用于SpEL表达式解析.
     */
    private final SpelExpressionParser spelExpressionParser = new SpelExpressionParser();
    /**
     * 用于获取方法参数定义名字.
     */
    private final DefaultParameterNameDiscoverer defaultParameterNameDiscoverer = new DefaultParameterNameDiscoverer();
    @Around("@annotation(org.platform.lwc.redis.annotation.Lock)")
    public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        Object object = null;
        RLock lock = null;
        try {
            // 获取注解实体信息
            Lock lockEntity = (((MethodSignature) proceedingJoinPoint.getSignature()).getMethod())
                    .getAnnotation(Lock.class);
            // 根据名字获取锁实例
            lock = redissonClient.getLock(getRediskey(proceedingJoinPoint));
            if (Objects.nonNull(lock)) {
                if (lock.tryLock(lockEntity.maxSleepMills(), lockEntity.keepMills(), TimeUnit.SECONDS)) {
                    object = proceedingJoinPoint.proceed();
                } else {
                    throw new RuntimeException();
                }
            }
        } finally {
            if (Objects.nonNull(lock) && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
        return object;
    }


    /**
     * 获取拦截到的请求方法  key 定义在注解上，支持SPEL表达式
     * @param joinPoint 切点
     * @return redisKey
     */
    private String getRediskey(ProceedingJoinPoint joinPoint) {
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method targetMethod = methodSignature.getMethod();
        Object target = joinPoint.getTarget();
        Object[] arguments = joinPoint.getArgs();
        Lock annotation = AnnotationUtils.findAnnotation(targetMethod, Lock.class);
        String spel=null;
        if(annotation != null){
            spel = annotation.key();
        }
        return SpelUtil.parse(target,spel, targetMethod, arguments);
    }
}